﻿using System;
using System.Diagnostics;
using System.Data;
using System.Reflection;
using System.Windows.Forms;
using System.IO;

using TypeFinder.Properties;

namespace TypeFinder
{
    public partial class Form1 : Form 
    {
        #region Private Members

        private TypeFinder typeFinder;
        private Type baseType;
        
        readonly TypeFinderDataSet.TypeInfoTableDataTable table = new TypeFinderDataSet.TypeInfoTableDataTable();
        private SystemMenu systemMenu;
        // ID constants
        private const int aboutSystemMenuID = 0x100;
        
        #endregion

        #region Constructor
        public Form1()
        {
            InitializeComponent();
            
            if(Environment.Is64BitProcess)
            {
                //Not interested in localization...
                Text += " x64";
            }
            
            //Don't do this binding via properties....causes problem with Obfuscation
            typeNameTextBox.Text = Settings.Default.LastSearchPattern;
            serchLocationTextBox.Text = Settings.Default.LastSearchDirectory;

            StartPosition = FormStartPosition.CenterScreen;
            searchTypeComboBox.SelectedIndex = 0;
            accessibilityComboBox.SelectedIndex = 0;
            itemTypeComboBox.SelectedIndex = 0;
            matchCriteriaComboBox.SelectedIndex = 0;

            dataGridView1.DataSource = table;
            dataGridView1.Columns[0].Width = 250;
            dataGridView1.Columns[1].Width = 60;
            dataGridView1.Columns[2].Width = 250;
            dataGridView1.Columns[3].Width = 300;
            dataGridView1.Columns[4].Width = 300;
            Load += Form1_Load;
        }

        void Form1_Load(object sender, EventArgs e) 
        {
            try
            {
                systemMenu = SystemMenu.FromForm(this);
                // Add a separator ...
                systemMenu.AppendSeparator();
                // ... and an "About" entry
                systemMenu.AppendMenu(aboutSystemMenuID, "About Type Finder");
            }
            catch (NoSystemMenuException /* err */ )
            {
                // Do some error handling
            }
        }

        protected override void WndProc(ref Message msg)
        {
            if (msg.Msg == (int)WindowMessages.wmSysCommand)
            {
                switch (msg.WParam.ToInt32())
                {
                    case aboutSystemMenuID:
                        { // Our about id
                            AboutBox1 frm = new AboutBox1();
                            frm.ShowDialog();
                        } break;
                }
            }
            // Call base class function
            base.WndProc(ref msg);
        }
        #endregion

        #region Event Handlers
        void EnableDisableSearchButtonOnInput()
        {
            searchButton.Enabled =
                !String.IsNullOrWhiteSpace(serchLocationTextBox.Text) &&
                Directory.Exists(serchLocationTextBox.Text) &&
                (!String.IsNullOrWhiteSpace(typeNameTextBox.Text) ||
                 !String.IsNullOrWhiteSpace(baseClassTextBox.Text));
        }

        private void TypeNameTextBoxTextChanged(object sender, EventArgs e) 
        {
            EnableDisableSearchButtonOnInput();
        }

        private void SerchLocationTextBoxTextChanged(object sender, EventArgs e) 
        {
            EnableDisableSearchButtonOnInput();
        }

        private void browseFolderButton_Click(object sender, EventArgs e)
        {
            if(DialogResult.OK == folderBrowserDialog1.ShowDialog())
            {
                serchLocationTextBox.Text = folderBrowserDialog1.SelectedPath;
            }
        }

        private void SearchButtonClick(object sender, EventArgs e)
        {
            StartSearch();
        }
        
        private void CancelButtonClick(object sender, EventArgs e) 
        {
            if(groupBox1.Enabled) 
            {
                Close();
            }
            else
            {
                if(typeFinder != null) 
                    typeFinder.Cancel();
            }
        }

        private void OnFormClosing(object sender, FormClosingEventArgs e)
        {
            Settings.Default.LastSearchPattern = typeNameTextBox.Text;
            Settings.Default.LastSearchDirectory = serchLocationTextBox.Text;
            Settings.Default.Save();
        }

        private void derivedFromCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            if (!derivedFromCheckBox.Checked)
            {
                baseClassTextBox.Text = "";
                derivedFromCheckBox.Enabled = false;
            }
            EnableDisableSearchButtonOnInput();
        }

        private void dataGridView1_SizeChanged(object sender, EventArgs e)
        {
            var totalSize = dataGridView1.Width -
                            dataGridView1.Columns[0].Width -
                            dataGridView1.Columns[1].Width -
                            dataGridView1.Columns[2].Width -
                            dataGridView1.Columns[3].Width -4;

            dataGridView1.Columns[4].Width = totalSize;
        }

        private void openContainingFolderToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var dataRowView = dataGridView1.SelectedRows[0].DataBoundItem as DataRowView;
            if (dataRowView == null) return;
            var row = dataRowView.Row as TypeFinderDataSet.TypeInfoTableRow;
            if (row == null) return;

            string args = string.Format("/Select, {0}", row.AssemblyPath);
            try
            {
                ProcessStartInfo pfi = new ProcessStartInfo("Explorer.exe", args);
                Process.Start(pfi);
            }
            catch(Exception)
            {
            }

        }

        private void FindDerivedTypes(object sender, EventArgs e)
        {
            var dataRowView = dataGridView1.SelectedRows[0].DataBoundItem as DataRowView;
            if(dataRowView == null) return;
            var row = dataRowView.Row as TypeFinderDataSet.TypeInfoTableRow;
            if (row == null) return;
            string baseClassName = row.AssemblyPath + ";" + row.FullName;

            baseClassTextBox.Text = baseClassName;

            lock (this) 
            {
                if(null != typeFinder) typeFinder.Cancel();
                Debug.WriteLine("FindDerivedTypes - Existing search stopped");
            }

            derivedFromCheckBox.Enabled = true;
            derivedFromCheckBox.Checked = true;
            typeNameTextBox.Text = "";
            StartSearch();
        }

        private void OnFindClassWithSameBaseType(object sender, EventArgs e) 
        {
            var dataRowView = dataGridView1.SelectedRows[0].DataBoundItem as DataRowView;
            if (dataRowView == null) return;
            var row = dataRowView.Row as TypeFinderDataSet.TypeInfoTableRow;
            if (row == null) return;

            Assembly assembly = Assembly.LoadFrom(row.AssemblyPath);
            Type type = assembly.GetType(row.FullName);
            var typeBaseType = type.BaseType;
            if (typeBaseType== null || typeBaseType == typeof(Object)) return;

            lock (this) 
            {
                if(null != typeFinder) typeFinder.Cancel();
                Debug.WriteLine("FindDerivedTypes - Existing search stopped");
            }

            baseClassTextBox.Text = typeBaseType.Assembly.Location + ";" + typeBaseType.FullName;
            derivedFromCheckBox.Enabled = true;
            derivedFromCheckBox.Checked = true;
            typeNameTextBox.Text = "";
            StartSearch();
        }
        private void contextMenuStrip1_Opening(object sender, System.ComponentModel.CancelEventArgs e)
        {
            openContainingFolderToolStripMenuItem.Enabled =
                searchDerivedTypesToolStripMenuItem.Enabled = dataGridView1.SelectedRows.Count == 1;
        }

        private void TimerTick(object sender, EventArgs e)
        {
            if(null == typeFinder) return;
            try
            {
                var currentSearcedAssembly = typeFinder.CurrentSearcedAssembly;
                if (!String.IsNullOrWhiteSpace(currentSearcedAssembly)) 
                {
                    searchStatusLabel.Text = String.Format("Searching: {0}", currentSearcedAssembly);
                }    
            }
            catch(NullReferenceException){ }
        }
        #endregion

        #region Search Handler
        private void StartSearch()
        {
            table.Rows.Clear();

            if (String.IsNullOrWhiteSpace(typeNameTextBox.Text) &&
                String.IsNullOrWhiteSpace(baseClassTextBox.Text)) return;
            if (String.IsNullOrWhiteSpace(serchLocationTextBox.Text)) return;

            if (!Directory.Exists(serchLocationTextBox.Text)) return;
            baseType = null;

            if (!String.IsNullOrWhiteSpace(baseClassTextBox.Text))
            {
                var parts = baseClassTextBox.Text.Split(';');
                if (parts.Length == 2)
                {
                    Assembly assembly = Assembly.LoadFrom(parts[0]);
                    baseType = assembly.GetType(parts[1]);
                }
                if (baseType == null) return;
            }

            if (null != typeFinder)
            {
                typeFinder.TypeFound -= TypeFinderTypeFound;
                typeFinder.SearchCompleted -= TypeFinderSearchCompleted;
            }

            typeFinder = new TypeFinder(typeNameTextBox.Text, itemTypeComboBox.Text,
                                        serchLocationTextBox.Text, searchTypeComboBox.Text,
                                        accessibilityComboBox.Text, baseType,
                                        matchCriteriaComboBox.Text);
            typeFinder.TypeFound += TypeFinderTypeFound;
            typeFinder.SearchCompleted += TypeFinderSearchCompleted;

            Debug.WriteLine("StartSearch - locking");
            lock (this)
            {
                Debug.WriteLine("StartSearch - locked");
                    
                searchButton.Enabled = groupBox1.Enabled = false;
                timer1.Start();
                typeFinder.SearchAsync();
                Debug.WriteLine("StartSearch - Search started async");
            }
            
        }

        void TypeFinderSearchCompleted(object sender, EventArgs e)
        {
            if (InvokeRequired)
            {
                Invoke(new EventHandler(TypeFinderSearchCompleted), sender, e);
                return;
            }

            TypeFinder currentFinder = sender as TypeFinder;
            if (null != currentFinder)
            {
                currentFinder.TypeFound -= TypeFinderTypeFound;
                currentFinder.SearchCompleted -= TypeFinderSearchCompleted;
            }

            if (null != currentFinder && ReferenceEquals(sender, typeFinder))
            {
                searchButton.Enabled = groupBox1.Enabled = true;
                searchStatusLabel.Text = @"Search completed";
                Debug.WriteLine("Inside TypeFinderSearchCompleted - signaling search completed event.");
                timer1.Stop();
            }
        }

        void TypeFinderTypeFound(object sender, TypeFoundEventArgs e) 
        {
            if(!ReferenceEquals(sender, typeFinder)) return;

            if(InvokeRequired)
            {
                BeginInvoke(new EventHandler<TypeFoundEventArgs>(TypeFinderTypeFound), sender, e);
                return;
            }
            var typeItem = e.TypeInfo;
            if (null == typeItem) return;
                
            Type type = typeItem.Type;
            string typeStr = typeItem.TypeStr;
            var row = table.NewTypeInfoTableRow();
            row.Type = typeStr;
            row.Name = type.Name;
            row.FullName = type.FullName;
            row.Assembly = Path.GetFileName(typeItem.AssemblyPath);
            row.AssemblyPath = typeItem.AssemblyPath;
            table.AddTypeInfoTableRow(row);
        }
        
        #endregion

        
    }
}
